package user

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"io/ioutil"
	"net/http"
	"strconv"
	"video-api/api/v1"
	"video-api/internal/consts"
	"video-api/internal/dao"
	"video-api/internal/middleware"
	"video-api/internal/model/entity"
	"video-api/internal/service"
	"video-api/utility"
	"video-api/utility/login_tool"
)

func init() {
	service.RegisterUserService(&userService{})
}

const googleTokenInfoURL = "https://oauth2.googleapis.com/tokeninfo"

type TokenInfo struct {
	Issuer        string `json:"iss"`
	Subject       string `json:"sub"`
	Audience      string `json:"aud"`
	Email         string `json:"email"`
	EmailVerified string `json:"email_verified"`
	Name          string `json:"name"`
	Picture       string `json:"picture"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Locale        string `json:"locale"`
	ExpiresIn     string `json:"expires_in"`
}

type userService struct{}

func (u *userService) GetUserInfoById(ctx context.Context, req *v1.UserInfoReq) (*entity.Users, error) {
	userModel := dao.Users.Ctx(ctx)
	ele := new(entity.Users)
	err := userModel.Where("id=?", req.UserId).Scan(ele)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, errors.New("用户不存在")
		}
		g.Log().Error(ctx, "userService-GetUserInfoById err: ", err, g.Map{"userId": req.UserId})
		return nil, err
	}
	return ele, nil
}

func (u *userService) LoginCallback(ctx context.Context, req *v1.LoginReq) (*v1.LoginRes, error) {
	// 获取登录方式（google、facebook等）
	provider, err := login_tool.GetProvider(req.Provider)
	if err != nil {
		g.Log().Error(ctx, "login-GetProvider err: ", err, g.Map{"provider": req.Provider})
		return nil, err
	}
	// 登录需要的凭证（Facebook登录需要赋值）
	session := provider.EmptySession(req.AccessToken)

	var user login_tool.User
	// 获取第三方用户信息
	user, err = provider.FetchUser(session)
	// 由于Facebook和Google的登录方式不一样，Facebook直接从前端返回Access Token，所以Facebook在上一步就可以直接拿到用户信息。需要code验证的则要通过Authorize方法获取
	if err != nil {
		// 获取第三方登录token
		_, err = session.Authorize(provider, req.Code)
		if err != nil {
			g.Log().Error(ctx, "login-Authorize err: ", err, g.Map{"credential": req.Code})
			return nil, err
		}
		// 获取第三方用户信息
		user, err = provider.FetchUser(session)
		if err != nil {
			g.Log().Error(ctx, "login-FetchUser err: ", err)
			return nil, err
		}
	}

	loginRes := new(v1.LoginRes)
	loginRes.LoginType = consts.LoginTypeMap[req.Provider]
	loginRes.UserCode = user.UserID
	// 将用户信息转化为jwt 存入redis中
	claimsInfo := middleware.ClaimsInfo{
		//UserId:        strconv.Itoa(int(userSelf.Id)),
		UserCode: user.UserID,
		Avatar:   user.AvatarURL,
		Email:    user.Email,
		NickName: user.NickName,
		//UserSource:    userSelf.Source,
		//UserLoginType: userSelf.LoginType,
		//Country:       userSelf.Country,
	}
	// 判断用户是否存在
	userModel := dao.Users.Ctx(ctx)
	userSelf := new(entity.Users)
	err = userModel.Where("id", req.Id).Scan(userSelf)
	//err = userModel.Where("user_code = ?", user.UserID).Scan(userSelf)
	if err != nil {
		if !errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, "login-getUserInfo err: ", err)
			return nil, err
		} else {
			g.Log().Warning(ctx, "login-getUserInfo is null")
		}
	}
	rq := g.RequestFromCtx(ctx)
	userSelf.NickName = user.NickName
	userSelf.Avatar = user.AvatarURL
	userSelf.LastLoginTime = gtime.Now()
	userSelf.Email = user.Email
	userSelf.UserCode = user.UserID
	userSelf.LatestLoginIp = utility.GetClientIp(rq)
	userSelf.LoginType = consts.LoginTypeMap[req.Provider]
	userSelf.IsLogin = consts.LOGIN_IN
	// 如果不存在，新增用户
	if userSelf.Id == 0 {
		userSelf.Id = utility.GetSnowflakeId()
		userSelf.Status = 1
		userSelf.LoginIp = utility.GetClientIp(rq)
		userSelf.CreatedAt = gtime.Now()
		//userSelf.Country = utility.GetCountryByIp(userSelf.LoginIp)
		userSelf.Country = "cn"          // 测试默认为cn
		userSelf.Source = consts.NATURAL // 默认自然流
		_, err = userModel.Save(userSelf)
		if err != nil {
			g.Log().Error(ctx, "login-SaveUserInfo err: ", err)
			return nil, err
		}
	} else {
		// 查找是否以前有绑定过，绑定过则返回以前绑定的账号
		userCodeModel := dao.Users.Ctx(ctx)
		userCodeSelf := new(entity.Users)
		err = userCodeModel.Where("user_code = ?", user.UserID).Scan(userCodeSelf)
		if err != nil && !errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, "user-LoginCallback err: ", err)
			return nil, err
		}
		if userCodeSelf.Id != 0 {
			userCodeSelf.NickName = user.NickName
			userCodeSelf.Avatar = user.AvatarURL
			userCodeSelf.LastLoginTime = gtime.Now()
			userCodeSelf.Email = user.Email
			userCodeSelf.UserCode = user.UserID
			userCodeSelf.LatestLoginIp = utility.GetClientIp(rq)
			userCodeSelf.LoginType = consts.LoginTypeMap[req.Provider]
			userCodeSelf.IsLogin = consts.LOGIN_IN
			// 存在第三方账号
			_, err = userModel.Data(userCodeSelf).Where("user_code = ?", user.UserID).Update()
			if err != nil {
				g.Log().Error(ctx, "login-UpdateUserInfo err: ", err)
				return nil, err
			}
			loginRes.IsLogin = userCodeSelf.IsLogin
			loginRes.Email = userCodeSelf.Email
			loginRes.UserId = userCodeSelf.Id
			loginRes.Username = userCodeSelf.NickName
			loginRes.AvatarURL = userCodeSelf.Avatar

			claimsInfo.UserId = strconv.Itoa(int(userCodeSelf.Id))
			claimsInfo.UserSource = userCodeSelf.Source
			claimsInfo.UserLoginType = userCodeSelf.LoginType
			claimsInfo.Country = userCodeSelf.Country
		} else {
			_, err = userModel.Data(userSelf).Where("id = ?", userSelf.Id).Update()
			if err != nil {
				g.Log().Error(ctx, "login-UpdateUserInfo err: ", err)
				return nil, err
			}
			loginRes.IsLogin = userSelf.IsLogin
			loginRes.Email = userSelf.Email
			loginRes.UserId = userSelf.Id
			loginRes.Username = userSelf.NickName
			loginRes.AvatarURL = userSelf.Avatar

			claimsInfo.UserId = strconv.Itoa(int(userSelf.Id))
			claimsInfo.UserSource = userSelf.Source
			claimsInfo.UserLoginType = userSelf.LoginType
			claimsInfo.Country = userSelf.Country
		}
	}

	if userSelf.CreatedAt != nil {
		claimsInfo.UserRegisterTime = float64(userSelf.CreatedAt.Timestamp())
	}
	tokenStr, err := middleware.GenerateToken(ctx, claimsInfo)
	if err != nil {
		g.Log().Error(ctx, "login-GenerateToken err: ", err)
		return nil, err
	}
	loginRes.Token = tokenStr
	// 将用户信息注入到上下文，提供给下游方法使用
	ctx = context.WithValue(ctx, consts.USER_JWT_KEY, &claimsInfo)
	rq.Request = rq.Request.WithContext(ctx)
	// 记录日志
	go func(ctx2 context.Context, loginParam *v1.LoginRes) {
		userLogsModel := dao.UserLogs.Ctx(ctx2)
		userLogsModel.Save(&entity.UserLogs{
			UserId:    loginParam.UserId,
			Version:   "v1",
			UserType:  loginRes.LoginType,
			CreatedAt: gtime.Now(),
			UpdatedAt: gtime.Now(),
			UserCode:  loginRes.UserCode,
		})
	}(ctx, loginRes)
	return loginRes, nil
}

// CreateUser 游客登录创建用户
func (u *userService) CreateUser(ctx context.Context, req *v1.CreateUserReq) (*v1.LoginRes, error) {
	// 查看是否已经存在该设备
	userModel := dao.Users.Ctx(ctx)
	userSelf := new(entity.Users)
	err := userModel.Where("device_id = ?", req.DeviceId).Scan(userSelf)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, "login-CreateUser GetUser err: ", err)
		} else {
			return nil, err
		}
	}
	rq := g.RequestFromCtx(ctx)
	if userSelf.Id == 0 {
		userSelf.Id = utility.GetSnowflakeId()
		userSelf.Status = 1
		userSelf.LoginType = consts.VISITOR
		userSelf.LoginIp = utility.GetClientIp(rq)
		userSelf.LatestLoginIp = utility.GetClientIp(rq)
		userSelf.CreatedAt = gtime.Now()
		userSelf.UpdatedAt = gtime.Now()
		userSelf.ProductId = req.ProductId
		userSelf.LastLoginTime = gtime.Now()
		//userSelf.Country = utility.GetCountryByIp(userSelf.LoginIp)
		userSelf.Country = "cn"          // 测试默认为cn
		userSelf.Source = consts.NATURAL // 默认自然流
		userSelf.DeviceId = req.DeviceId
		userSelf.IsLogin = consts.LOGIN_OUT
		_, err = userModel.Save(userSelf)
		if err != nil {
			g.Log().Error(ctx, "login-SaveUserInfo err: ", err)
			return nil, err
		}
	} else {
		userSelf.LatestLoginIp = utility.GetClientIp(rq)
		userSelf.LastLoginTime = gtime.Now()
		//userSelf.UserCode = ""
		//userSelf.Email = ""
		//userSelf.NickName = ""
		//userSelf.Avatar = ""
		userSelf.IsLogin = consts.LOGIN_OUT
		_, err = userModel.Data(userSelf).Where("id = ?", userSelf.Id).Update()
		if err != nil {
			g.Log().Error(ctx, "login-SaveUserInfo err: ", err)
			return nil, err
		}
	}
	loginRes := &v1.LoginRes{
		UserId:    userSelf.Id,
		Username:  strconv.Itoa(int(userSelf.Id)),
		IsLogin:   userSelf.IsLogin,
		LoginType: consts.VISITOR,
	}
	// 将用户信息转化为jwt 存入redis中
	claimsInfo := middleware.ClaimsInfo{
		UserId:        strconv.Itoa(int(userSelf.Id)),
		UserSource:    userSelf.Source,
		UserLoginType: consts.VISITOR,
		Country:       userSelf.Country,
	}
	if userSelf.CreatedAt != nil {
		claimsInfo.UserRegisterTime = float64(userSelf.CreatedAt.Timestamp())
	}
	tokenStr, err := middleware.GenerateToken(ctx, claimsInfo)
	if err != nil {
		g.Log().Error(ctx, "login-GenerateToken err: ", err)
		return nil, err
	}
	// 将用户信息注入到上下文，提供给下游方法使用
	ctx = context.WithValue(ctx, consts.USER_JWT_KEY, &claimsInfo)
	rq.Request = rq.Request.WithContext(ctx)
	loginRes.Token = tokenStr
	return loginRes, nil
}

func (u *userService) Login(ctx context.Context, req *v1.LoginReq) (*v1.LoginRes, error) {

	// 调用 Google 的 tokeninfo 端点验证 JWT
	resp, err := http.Get(fmt.Sprintf("%s?id_token=%s", googleTokenInfoURL, req.Credential))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// 解析 Google 返回的用户信息
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var tokenInfo TokenInfo
	err = json.Unmarshal(body, &tokenInfo)
	if err != nil {
		return nil, err
	}

	// 在凭证的方式进行oauth验证，为了更加安全的判断是自己的应用产生的凭证，这一步是必须的
	clientID := "556853485104-slivi5no868loropj8rhq8g87j4od2gu.apps.googleusercontent.com"
	if tokenInfo.Audience != clientID {
		return nil, errors.New("invalid audience")
	}

	// 判断用户是否存在
	userModel := dao.Users.Ctx(ctx)
	userSelf := new(entity.Users)
	err = userModel.Where("user_code", tokenInfo.Subject).Scan(userSelf)
	if err != nil {
		if !errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, "login-getUserInfo err: ", err)
			return nil, err
		} else {
			g.Log().Warning(ctx, "login-getUserInfo is null")
		}
	}
	rq := g.RequestFromCtx(ctx)
	userSelf.NickName = tokenInfo.Name
	userSelf.Avatar = tokenInfo.Picture
	userSelf.LastLoginTime = gtime.Now()
	userSelf.Email = tokenInfo.Email
	userSelf.LatestLoginIp = utility.GetClientIp(rq)
	// 如果不存在，新增用户
	if userSelf.UserCode == "" {
		userSelf.Id = utility.GetSnowflakeId()
		userSelf.UserCode = tokenInfo.Subject
		userSelf.Status = 1
		userSelf.LoginType = req.Provider
		userSelf.LoginIp = utility.GetClientIp(rq)
		userSelf.CreatedAt = gtime.Now()
		//userSelf.Country = utility.GetCountryByIp(userSelf.LoginIp)
		userSelf.Country = "cn"          // 测试默认为cn
		userSelf.Source = consts.NATURAL // 默认自然流
		_, err = userModel.Save(userSelf)
		if err != nil {
			g.Log().Error(ctx, "login-SaveUserInfo err: ", err)
			return nil, err
		}
	} else {
		_, err = userModel.Data(userSelf).Where("id = ?", userSelf.Id).Update()
		if err != nil {
			g.Log().Error(ctx, "login-UpdateUserInfo err: ", err)
			return nil, err
		}
	}
	// 将用户信息转化为jwt 存入redis中
	claimsInfo := middleware.ClaimsInfo{
		UserId:        userSelf.UserCode,
		Avatar:        userSelf.Avatar,
		Email:         userSelf.Email,
		NickName:      userSelf.NickName,
		UserSource:    userSelf.Source,
		UserLoginType: userSelf.LoginType,
		Country:       userSelf.Country,
	}
	if userSelf.CreatedAt != nil {
		claimsInfo.UserRegisterTime = float64(userSelf.CreatedAt.Timestamp())
	}
	tokenStr, err := middleware.GenerateToken(ctx, claimsInfo)
	if err != nil {
		g.Log().Error(ctx, "login-GenerateToken err: ", err)
		return nil, err
	}
	return &v1.LoginRes{
		Username:  userSelf.NickName,
		UserId:    userSelf.Id,
		Email:     userSelf.Email,
		AvatarURL: userSelf.Avatar,
		Token:     tokenStr,
	}, nil

	//return nil, nil
}

func (u *userService) LoginOut(ctx context.Context) (*v1.LoginRes, error) {
	// 退出登录就是将第三方登录置换成游客登录
	info := ctx.Value("userInfo").(*middleware.ClaimsInfo)
	if info == nil {
		return nil, errors.New("用户信息获取失败")
	}
	userModel := dao.Users.Ctx(ctx)
	userSelf := new(entity.Users)
	err := userModel.Where("id = ?", info.UserId).Scan(userSelf)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			g.Log().Error(ctx, " userService-LoginOut-GetUser err: ", err)
		} else {
			return nil, err
		}
	}
	rq := g.RequestFromCtx(ctx)
	// 将当前账号设置为登出
	userSelf.IsLogin = consts.LOGIN_OUT
	_, err = userModel.Data(userSelf).Where("id = ?", userSelf.Id).Update()
	if err != nil {
		g.Log().Error(ctx, "userService-LoginOut-UpdateUserInfo err: ", err)
		return nil, err
	}

	loginRes := &v1.LoginRes{
		UserId:   userSelf.Id,
		Username: strconv.Itoa(int(userSelf.Id)),
		Token:    "",
		IsLogin:  userSelf.IsLogin,
	}
	// 将用户信息转化为jwt 存入redis中
	claimsInfo := middleware.ClaimsInfo{
		UserId:        strconv.Itoa(int(userSelf.Id)),
		UserSource:    userSelf.Source,
		UserLoginType: userSelf.LoginType,
		Country:       userSelf.Country,
	}
	if userSelf.CreatedAt != nil {
		claimsInfo.UserRegisterTime = float64(userSelf.CreatedAt.Timestamp())
	}
	tokenStr, err := middleware.GenerateToken(ctx, claimsInfo)
	if err != nil {
		g.Log().Error(ctx, "userService-LoginOut-GenerateToken err: ", err)
		return nil, err
	}
	// 将用户信息注入到上下文，提供给下游方法使用
	ctx = context.WithValue(ctx, consts.USER_JWT_KEY, &claimsInfo)
	rq.Request = rq.Request.WithContext(ctx)
	loginRes.Token = tokenStr
	return loginRes, nil
}

// AutoPlay 设置自动播放
func (u *userService) AutoPlay(ctx context.Context, req *v1.AutoPlayReq) error {
	_, err := dao.Users.Ctx(ctx).Data(g.Map{"auto_play": req.AutoPlay}).
		Where("id = ?", req.UserId).Update()
	if err != nil {
		return err
	}
	return err
}
